package com.qiguliuxing.dts.job.task;

import com.dangdang.ddframe.job.api.ShardingContext;
import com.dangdang.ddframe.job.api.simple.SimpleJob;
import com.google.common.collect.Maps;
import com.qiguliuxing.dts.common.constant.CommConsts;
import com.qiguliuxing.dts.common.type.OrderStatusEnum;
import com.qiguliuxing.dts.db.domain.DtsOrder;
import com.qiguliuxing.dts.db.domain.DtsOrderGoods;
import com.qiguliuxing.dts.service.base.DtsGoodsProductService;
import com.qiguliuxing.dts.service.base.DtsOrderGoodsService;
import com.qiguliuxing.dts.service.base.DtsOrderService;
import com.qiguliuxing.dts.service.seckill.cache.SeckillCache;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

import static org.slf4j.LoggerFactory.getLogger;

/**
 * 未支付订单取消job
 *  检查半小时内未支付的订单，并取消
 * @author qiguliuxing
 * @since 1.0.0
 */
@Component
public class UnpaidOrderCancelJob implements SimpleJob {
    private static final Logger logger = getLogger(UnpaidOrderCancelJob.class);

    @Autowired
    private DtsOrderGoodsService orderGoodsService;
    @Autowired
    private DtsOrderService orderService;
    @Autowired
    private DtsGoodsProductService productService;

    @Autowired
    private SeckillCache seckillCache;

    /**
     * 执行作业.
     *
     * @param shardingContext 分片上下文
     */
    @Override
    public void execute(ShardingContext shardingContext) {
        logger.info(String.format("Thread ID: %s, 作业分片总数: %s, " +
                        "当前分片项: %s.当前参数: %s," +
                        "作业名称: %s.作业自定义参数: %s",
                Thread.currentThread().getId(),
                shardingContext.getShardingTotalCount(),
                shardingContext.getShardingItem(),
                shardingContext.getShardingParameter(),
                shardingContext.getJobName(),
                shardingContext.getJobParameter()
        ));

        Map<String, Object> paramsMap = Maps.newHashMapWithExpectedSize(CommConsts.INIT_HASH_MAP_SIZE);
        paramsMap.put("shardingTotalCount", shardingContext.getShardingTotalCount());
        paramsMap.put("shardingItem", shardingContext.getShardingItem());
        LocalDateTime expiredPointTime = LocalDateTime.now().minusMinutes(CommConsts.UNPAID_ORDER_EXPIRE_MINUTES);
        paramsMap.put("expiredPointTime", expiredPointTime);
        paramsMap.put("orderStatus", OrderStatusEnum.CREATE.status);
        List<DtsOrder> orderList = orderService.queryUnpaid(paramsMap);
        for (DtsOrder order : orderList) {
            // 设置订单已取消状态
            order.setOrderStatus(OrderStatusEnum.AUTO_CANCEL.status);
            order.setEndTime(LocalDateTime.now());
            if (orderService.updateWithOptimisticLocker(order) == 0) {
                throw new RuntimeException("更新数据已失效");
            }

            // 商品货品数量增加
            Integer orderId = order.getId();
            List<DtsOrderGoods> orderGoodsList = orderGoodsService.queryByOid(orderId);
            for (DtsOrderGoods orderGoods : orderGoodsList) {
                Integer productId = orderGoods.getProductId();

                // 秒杀订单标识
                BigDecimal seckillReducePrice = orderGoods.getSeckillReducePrice();
                boolean isNotSeckill = seckillReducePrice.compareTo(BigDecimal.ZERO)==0;
                addStock(orderGoods, productId, isNotSeckill);
            }
            logger.info("订单 ID={} 已经超期自动取消订单", order.getId());
        }
    }

    /**
     * 增加库存数据
     * @param orderGoods
     * @param productId
     * @param isNotSeckill
     */
    private void addStock(DtsOrderGoods orderGoods, Integer productId, boolean isNotSeckill) {
        Short number = orderGoods.getNumber();
        if (isNotSeckill) {
            if (productService.addStock(productId, number) == 0) {
                throw new RuntimeException("商品货品库存增加失败");
            }
        } else {
            /**
             * 秒杀订单取消的库存恢复其实只需要更新缓存中的货品库存即可
             * 缓存中的库存会通过定时器同步到 DB, 秒杀库存已缓存的为准
             */
            /*if (productService.addSeckillStock(productId, number) == 0) {
                throw new RuntimeException("秒杀库存增加失败");
            }*/
            try {
                seckillCache.addStock(productId, number);
            } catch (Exception e) {
                throw new RuntimeException("秒杀货品库存增加失败");
            }

        }
    }
}
